package com.supreme.stockserver.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.supreme.common.dto.UpdateGoodsStockDTO;
import com.supreme.commonmybatis.mapper.GoodsMapper;
import com.supreme.commonmybatis.mapper.StockMapper;
import com.supreme.commonmybatis.mapper.StockRecordMapper;
import com.supreme.commonmybatis.entity.primary.MBGoods;
import com.supreme.commonentity.mysql.primary.BizStock;
import com.supreme.commonentity.mysql.primary.BizStockRecord;
import com.supreme.commonboot.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
public class StockService {

    @Resource
    private GoodsMapper goodsMapper;

    @Resource
    private StockMapper stockMapper;

    @Resource
    private StockRecordMapper stockRecordMapper;

    public List<BizStockRecord> selectStockRecordList(BizStockRecord stockRecordQuery) {
        final LambdaQueryWrapper<BizStockRecord> queryWrapper = Wrappers.lambdaQuery(BizStockRecord.class);
        if (stockRecordQuery.getStockId() != null) {
            queryWrapper.eq(BizStockRecord::getStockId, stockRecordQuery.getStockId());
        }
        if (stockRecordQuery.getUserId() != null) {
            queryWrapper.eq(BizStockRecord::getUserId, stockRecordQuery.getUserId());
        }
        if (stockRecordQuery.getOrderId() != null) {
            queryWrapper.eq(BizStockRecord::getOrderId, stockRecordQuery.getOrderId());
        }
        return stockRecordMapper.selectList(queryWrapper);
    }

    @Transactional(rollbackFor = BizException.class)
    public void createStock(Long goodsId, Integer stockNum) {
        final MBGoods goods = goodsMapper.selectById(goodsId);
        if (goods == null) {
            throw new BizException("GOODS NOT FOUND");
        }

        final BizStock stock = new BizStock()
                .setGoodsId(goodsId)
                .setStockType(1)
                .setStockNum(stockNum);
        stockMapper.insert(stock);

        final BizStockRecord stockRecord = new BizStockRecord()
                .setStockId(stock.getStockId())
                .setRecordNum(stockNum)
                .setRecordType(0);
        stockRecordMapper.insert(stockRecord);
    }

    @Transactional(rollbackFor = BizException.class)
    public void updateStock(Long stockId, Long userId, Long orderId, Integer stockNum) {
        final BizStock stock = stockMapper.selectById(stockId);
        if (stock == null) {
            throw new BizException("STOCK NOT FOUND");
        }

        Integer newStockNum = stock.getStockNum() + stockNum;
        stock.setStockNum(newStockNum);
        stockMapper.updateById(stock);

        Integer recordType = stockNum >= 0 ? 1 : 2;
        final BizStockRecord stockRecord = new BizStockRecord()
                .setStockId(stock.getStockId())
                .setUserId(userId)
                .setOrderId(orderId)
                .setRecordType(recordType)
                .setRecordNum(stockNum);
        stockRecordMapper.insert(stockRecord);
    }

    public List<BizStock> getGoodsStock(Long goodsId, Integer stockNum) {
        final LambdaQueryWrapper<BizStock> queryWrapper = Wrappers.lambdaQuery(BizStock.class).eq(BizStock::getGoodsId, goodsId);
        if (Objects.nonNull(stockNum)) {
            queryWrapper.ge(BizStock::getStockNum, stockNum);
        }
        return stockMapper.selectList(queryWrapper);
    }

    public int updateStockNum(Long stockId, Integer stockNum) {
        BizStock stock = new BizStock().setStockNum(stockNum);
        LambdaQueryWrapper<BizStock> queryWrapper = Wrappers.lambdaQuery(BizStock.class)
                .eq(BizStock::getStockId, stockId);
        return stockMapper.update(stock, queryWrapper);
    }

    /**
     * 分布式扣减库存
     * @param updateGoodsStockDTO
     */
    @Transactional(rollbackFor = BizException.class)
    public Integer updateGoodsStock(UpdateGoodsStockDTO updateGoodsStockDTO) {
        final Long goodsId = updateGoodsStockDTO.getGoodsId();
        final Integer goodsNum = updateGoodsStockDTO.getGoodsNum();

        int retryNum = 3;
        for (int i = 0; i < retryNum; i++) {
            final LambdaQueryWrapper<BizStock> queryWrapper = Wrappers.lambdaQuery(BizStock.class)
                    .eq(BizStock::getGoodsId, goodsId)
                    .ge(BizStock::getStockNum, goodsNum);
            final BizStock stock = stockMapper.selectOne(queryWrapper);
            if (Objects.isNull(stock)) {
                throw new BizException("库存不足，购买失败！！！");
            }

            final Long stockId = stock.getStockId();
            final Integer version = stock.getVersion();
            stock.setStockNum(stock.getStockNum() - goodsNum);
            stock.setVersion(version + 1);

            final LambdaQueryWrapper<BizStock> updateQueryWrapper = Wrappers.lambdaQuery(BizStock.class)
                    .eq(BizStock::getStockId, stockId)
                    .eq(BizStock::getVersion, version);
            final int updateCount = stockMapper.update(stock, updateQueryWrapper);

            if (updateCount == 0) {
                log.info("数据被修改，本次购买失败，继续尝试！！！");
                continue;
            }

            BizStockRecord bizStockRecord = new BizStockRecord()
                    .setStockId(stockId)
                    .setRecordNum(goodsNum)
                    .setRecordType(1)
                    .setOrderId(1L)
                    .setUserId(1L);
            stockRecordMapper.insert(bizStockRecord);

            return 0;
        }

        throw new BizException("库存更新失败！！！");
    }
}
